package com.tsh.xml.converter;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List; 
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.tsh.xml.anno.XmlElementExcept;
import com.tsh.xml.anno.XmlElementReName;
import com.tsh.xml.entity.Element;
 


public class PacketConverter {
 
	
	/**
	 * 根据xml和bean对应的class 转换为Bean对象
	 * @param xml
	 * @param beanClass
	 * @param baseClassMap
	 * @return
	 */
	public   <T> T convertToJavaBean(String xml, Class<T> beanClass,Map<String,String> baseClassMap) {  
        T t = null;  
        try {  
        	xml = xml.substring(xml.indexOf("?>")+2); //去掉头部
        	String mapClass = baseClassMap.get(beanClass.getName());
        	if(mapClass!= null){
        		beanClass  = (Class<T>) Class.forName(mapClass);
        	}
        	//将xml解析到element树结构中
        	Element element =  new Element();
        	this.loadXmlToElements(element, xml);
        	//将树结构按照beanClass进行bean转换，如果是继承类型 或者List类型 需在baseClassMap中指明泛型对象
        	t = (T)this.convertToJavaBean(beanClass, element.getChilds().get(0), baseClassMap);
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return t;  
    }
	
	/**
	 * 将javabean对象输出为XML格式
	 * @param object
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public String convertToXML(Object object) throws IllegalArgumentException, IllegalAccessException{
		String head ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n";
		return head+this.convertToXML(object,0);
	}
	
	
	/**
	 * 根据element节点 转换类型beanClass ，子类型映射列表 将xml节点转换为
	 * @param beanClass 待转换的类型
	 * @param element	xml转换的节点元素
	 * @param baseClassMap 子类型映射列表
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 */
	private   Object convertToJavaBean(Class<?>  beanClass , Element element,Map<String,String> baseClassMap) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
		 //实例化当前节点  
		 Object  obj = beanClass.newInstance(); 
		 //根据子节点填充数据
			 for (Element child : element.getChilds()) {
				 //根据节点获取对象中对应的属性
				 Field field=this.searchFieldElementName(beanClass, child.getName(),baseClassMap);
				 field.setAccessible(true);
				 if(!child.isLeafed()){//非叶子节点，则找映射深入 
					 if(field.getType() == java.util.List.class){
						 //list类型根据list的名字获取对应的泛型的class类型
						 String fieldClassName = this.getClassName(field.getName(), baseClassMap);
						 //将子信息封装到List中
						 List list = new ArrayList();
						 for(Element listElement :child.getChilds()){
							 //递归调用
							 Object elementValue = this.convertToJavaBean(Class.forName(fieldClassName), listElement, baseClassMap);
							 list.add(elementValue);
						 }
						 field.set(obj, list);
					 }else{
						 //按照类名取的
						 String fieldClassName = this.getClassName(field.getType().getName(), baseClassMap);
						 //递归调用
						 Object elementValue = this.convertToJavaBean(Class.forName(fieldClassName), child, baseClassMap);
						 field.set(obj,elementValue);
					 }
				 }else{ //叶子节点
					 //设置普通字段的值
				     field.set(obj,child.getText());
				 }
			 } 
		 return obj;
	}
	
	/**
	 * 根据节点名称匹配寻找对应类的字段
	 * @param beanClass
	 * @param searchName
	 * @param baseClassMap
	 * @return
	 * @throws ClassNotFoundException 
	 */
	private Field searchFieldElementName(Class<?>  beanClass,String searchName,Map<String,String> baseClassMap) throws ClassNotFoundException{
		 
		Field field = null ;
		try {
			//尝试根据名称直接查找
			field=beanClass.getDeclaredField(searchName);
		} catch (Exception e) {
			//根据字段名称找不到则反向映射
			Set<String> set = baseClassMap.keySet(); 
			Field[] fields = beanClass.getDeclaredFields();
			for (String key : set) {
				//若配置了类别名又是抽象子类 从配置的实现类中找字段
				for (Field fd : fields) { 
					if(fd.getType().getName().endsWith(key)){
						XmlElementReName xmlReName = Class.forName(baseClassMap.get(key)).getAnnotation(XmlElementReName.class);
						if(xmlReName != null && xmlReName.name().equals(searchName)){
							field = fd;
							break;
						}
					}
				}
				
				//如果是list
				if(field==null)
				if(baseClassMap.get(key).endsWith(searchName)){
					searchName = key; //转换为父类的类型
					break;
				}
			}
			
			if(field==null)
			for (Field fd : fields) { 
				//从注解中匹配或者从类型中匹配
				if(searchName.equals(this.getFieldSerchName(fd)) || fd.getType().getName().endsWith(searchName)){
					field = fd;
					break;
				}
			}
			
			//最终以类上配置的别名为准
			if(field==null)
			for (Field fd : fields) { 
				XmlElementReName xmlReName = fd.getType().getAnnotation(XmlElementReName.class);
				//从注解中匹配或者从类型中匹配
				if(xmlReName != null){
					field = fd;
					break;
				}
			}
		}
		if(field == null ){
			System.out.println("aa");
		}
		return field;
	}
	
	
	/**
	 * 从映射集中获取xml的节点名称取对应实际的映射类型
	 * @param fieldClass
	 * @param baseClassMap 1.配置所有list类型的 变量名=泛型名 映射,
	 * 					   2.配置所有的 抽象类/父类 = 实际类型 .
	 * @return
	 */
	private String getClassName(String key,Map<String,String> baseClassMap){
		String realClassName = baseClassMap.get(key);
		if(realClassName == null){
			realClassName = key;
		}
		return realClassName;
	}
	
	
	
	
 
	/**
	 * 将javabean对象输出为XML格式 的具体实现
	 * @param object
	 * @param level 
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	private   String convertToXML(Object object,int level) throws IllegalArgumentException, IllegalAccessException{
		Class  xmlBeanClass = object.getClass();
		StringBuffer xmlStr=new StringBuffer(); 
//		String rootElementName = object.getClass().getSimpleName();
		String rootElementName = this.getClassSerchName(object.getClass());
		//加入普通节点
		this.appendSpace(level, xmlStr);
		xmlStr.append(this.getPrefix(rootElementName));
		xmlStr.append("\r\n"); 
		Field[] fields = xmlBeanClass.getDeclaredFields();
		for (Field field : fields) {
			String name = this.getFieldSerchName(field);
			if(name == null){ //被排除的
				continue;
			}
			//设置可见性
			field.setAccessible(true);
			
			if(field.getType() == java.util.List.class){
				//list对象
				this.appendSpace((level+1), xmlStr);
				xmlStr.append(this.getPrefix(name));
				xmlStr.append("\r\n"); 
				//循环加子列表
				List list = (List)field.get(object);
				for (Object listObject : list) {
					  //递归
					  String result = this.convertToXML(listObject,(level+2));
					  xmlStr.append(result);
				}
				this.appendSpace((level+1), xmlStr);
				xmlStr.append(this.getSuffix(name));
				xmlStr.append("\r\n");  
				
			}else if(field.getType().getName().startsWith("java.util") || field.getType().getName().startsWith("java.lang")){
				//值对象取值
				 Object value = field.get(object) ; 
					 if(value==null){
						 value = "";
					 } 
					 this.appendSpace((level+1), xmlStr);
						xmlStr.append(this.getPrefix(name)); 
						xmlStr.append(value);
						xmlStr.append(this.getSuffix(name));
						xmlStr.append("\r\n"); 
				 
			}else{
				Object fieldObj = field.get(object);
				//递归调用
				String result = this.convertToXML(fieldObj,(level+1));
			    xmlStr.append(result);
			}
		}
		//加入结束节点
		this.appendSpace(level, xmlStr);
		xmlStr.append(this.getSuffix(rootElementName));
		xmlStr.append("\r\n"); 
		return xmlStr.toString();
	}
	 
	 
	/**
	 * 将xml字符串解析到树节点中
	 * @param xml
	 */
	public void loadXmlToElements(Element parentElement , String xml){
		Element thisElement = new Element();
		thisElement.setParentElement(parentElement);
		xml = xml.trim(); //取除空格
		int start = xml.indexOf("<");//第一个节点起始位置
		int end = xml.indexOf(">");//第一个节点结束位置
		String slipStr = xml.substring(start+1, end).trim();
		String elementName=null;
		boolean isSimpleElement = false;
		if(slipStr.endsWith("/")){ //如果是单元素节点
			slipStr = slipStr.substring(0,slipStr.length()-1); //去掉尾部
			isSimpleElement =true;
		} 
		elementName=this.getElementName(slipStr); //获取元素名称
		if(elementName.length()<slipStr.length()){ //有属性
				slipStr=slipStr.substring(elementName.length());//去掉名字部分
				thisElement.setAttributes(this.getAttributes(slipStr));
		}
		thisElement.setName(elementName);//设置元素名称
	 
		if(!isSimpleElement){ //
			//继续追查子节点
			String xmlPre = xml.substring(end+1, xml.indexOf(this.getSuffix(elementName)));//去掉尾部 获得子信息
			if(xmlPre.trim().startsWith("<")){ //有子元素
				//继续查找子元素
				this.loadXmlToElements(thisElement, xmlPre);
			}else{//就是文本
				thisElement.setText(xmlPre);
				thisElement.setLeafed(true); //叶子节点
			}
			xml = xml.substring(xml.indexOf(this.getSuffix(elementName))+this.getSuffix(elementName).length());
			//查找兄弟元素
			if(xml.trim().length()>0){
				this.loadXmlToElements(parentElement, xml);
			}
			 
		}
		parentElement.getChilds().add(thisElement);
	}
	
	/**
	 * 将拼装好的Element对象输出为json格式
	 * @param element
	 * @param level
	 * @return
	 */
	public String printElementToJSON(Element element,int level){
		StringBuffer jsonStr = new StringBuffer();
		this.appendSpace(level, jsonStr);
		if(element.isLeafed()){
			jsonStr.append("\""+element.getName()+"\" : \""+element.getText()+"\",");
		}else{
			jsonStr.append(""+element.getName()+":{");
			this.appendEnter(jsonStr);
			for (Element chiled :element.getChilds()) {
				jsonStr.append(this.printElementToJSON(chiled, level+1));
				this.appendEnter(jsonStr);
			}
			this.appendSpace(level, jsonStr);
			jsonStr.append("},");
		} 
		return jsonStr.toString();
	}
	
	
	//获取该字段应被匹配的名称
	private String getFieldSerchName(Field field){
		 XmlElementReName xmlElementReName = field.getAnnotation(XmlElementReName.class); //重命名
		 XmlElementExcept xmlElementExcept = field.getAnnotation(XmlElementExcept.class); //排除
		 if(xmlElementExcept != null){
			 return null;
		 }else if(xmlElementReName != null){
			 return xmlElementReName.name(); 
		 }
		 return field.getName(); //字段名称
	}
 
	//获取该类应被匹配的名称
	private String getClassSerchName(Class<?> beanClass){
		 XmlElementReName xmlElementReName = beanClass.getAnnotation(XmlElementReName.class); //重命名 
		  if(xmlElementReName != null){
			 return xmlElementReName.name(); 
		 }
		 return beanClass.getSimpleName(); //字段名称
	}
 
	
	
	/**
	 * 获取元素名称
	 * @param slipStr
	 * @return
	 */
	private String getElementName(String slipStr){
		int len = slipStr.indexOf(" ");
		if(len==-1){
			len=slipStr.length();
		}
		return slipStr.substring(0,len); //元素名称
	}
	/**
	 * 获取元素行中的属性
	 * @param slipStr
	 * @return
	 */
	private Map<String,String> getAttributes(String slipStr){
		Map<String,String>  attributesMap= new ConcurrentHashMap<String, String>();
		String[] attributes=slipStr.split(" ");
		for (String attribute : attributes) {
			String[] items = attribute.split("=");
			if(items.length>1){
				attributesMap.put(items[0], items[1]);
			}else{
				attributesMap.put(items[0], null);
			}
		}
		return attributesMap;
	}
	
	/**
	 * 根据level打印格式
	 * @param level
	 * @param str
	 */
	private void appendSpace(int level , StringBuffer str){
		String space="    ";
		for (int i = 0; i < level; i++) {
			 str.append(space);
		}
	}
	/**
	 * 增加换行
	 * @param level
	 * @param str
	 */
	private void appendEnter(StringBuffer str){
		str.append("\r\n");
	}
	//根据节点名称获取起始节点和结束节点
	private  String getPrefix(String elementName){
		return "<"+elementName+">";
	}
	private  String getSuffix(String elementName){
		return "</"+elementName+">";
	}
}
